home *** CD-ROM | disk | FTP | other *** search
/ Programming an RTS Game with Direct3D / Programming an RTS Game with Direct3D.iso / Examples / Chapter 15 / Example 15.2 / network.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2006-07-01  |  14.9 KB  |  518 lines

  1. #include "network.h"
  2. #include "app.h"
  3.  
  4. // {D962EEE5-7783-4018-9C0D-C326821F808F}, Unique application ID
  5. GUID RTS_APP_ID = { 0xd962eee5, 0x7783, 0x4018, {0x9c, 0xd, 0xc3, 0x26, 0x82, 0x1f, 0x80, 0x8f} };
  6.  
  7. NETWORK network;    //Global network variable
  8. extern APPLICATION app;
  9.  
  10. NETWORK::NETWORK()
  11. {
  12.     m_pServer = NULL;
  13.     m_pClient = NULL;
  14.     m_pMyAddress = NULL;
  15.     m_pServerAddress = NULL;
  16. }
  17.  
  18. NETWORK::~NETWORK()
  19. {
  20.     Release();
  21. }
  22.  
  23. void NETWORK::Init(bool _server, char _playerName[])
  24. {
  25.     try
  26.     {
  27.         server = _server;
  28.         CoInitialize(0);
  29.         strcpy(playerName, _playerName);
  30.         connected = false;
  31.  
  32.         if(server)        //Init Server
  33.         {
  34.             if(FAILED(CoCreateInstance(CLSID_DirectPlay8Server, NULL, CLSCTX_INPROC_SERVER, IID_IDirectPlay8Server, (LPVOID*)&m_pServer)))debug.Print("Failed to Create Server.\n");
  35.             if(FAILED(m_pServer->Initialize(NULL, ServerCallback, 0)))debug.Print("Failed to Init Server.\n");
  36.         }
  37.         else            //Init Client
  38.         {
  39.             if(FAILED(CoCreateInstance(CLSID_DirectPlay8Client, NULL, CLSCTX_INPROC_SERVER, IID_IDirectPlay8Client, (LPVOID*)&m_pClient)))debug.Print("Failed to Create Client.\n");
  40.             if(FAILED(m_pClient->Initialize(NULL, ClientCallback, 0)))debug.Print("Failed to Init Client.\n");
  41.         }
  42.  
  43.         //Create My address
  44.         if(FAILED(CoCreateInstance(CLSID_DirectPlay8Address, NULL, CLSCTX_ALL, IID_IDirectPlay8Address, (LPVOID*)&m_pMyAddress)))debug.Print("Failed to Create m_pMyAddress.\n");
  45.         if(FAILED(m_pMyAddress->SetSP(&CLSID_DP8SP_TCPIP)))debug.Print("Failed to set Service Protocol");
  46.  
  47.         //Create Server address
  48.         if(FAILED(CoCreateInstance(CLSID_DirectPlay8Address, NULL, CLSCTX_ALL, IID_IDirectPlay8Address, (LPVOID*)&m_pServerAddress)))debug.Print("Failed to Create m_pServerAddress.\n");
  49.         if(FAILED(m_pServerAddress->SetSP(&CLSID_DP8SP_TCPIP)))debug.Print("Failed to set Service Protocol");
  50.  
  51.         CoUninitialize();
  52.     }
  53.     catch(...)
  54.     {
  55.         debug.Print("Error in NETWORK::Init()");
  56.     }
  57. }
  58.  
  59. void NETWORK::Release()
  60. {
  61.     try
  62.     {
  63.         if(m_pMyAddress)m_pMyAddress->Release();
  64.         if(m_pServerAddress)m_pServerAddress->Release();
  65.         m_pMyAddress = NULL;
  66.         m_pServerAddress = NULL;        
  67.  
  68.         //Close connections
  69.         if(m_pServer)
  70.         {
  71.             m_pServer->Close(DPNCLOSE_IMMEDIATE);
  72.             m_pServer = NULL;
  73.         }
  74.         if(m_pClient)
  75.         {
  76.             m_pClient->Close(DPNCLOSE_IMMEDIATE);
  77.             m_pClient = NULL;
  78.         }
  79.     }
  80.     catch(...)
  81.     {
  82.         debug.Print("Error in NETWORK::Release()");
  83.     }
  84. }
  85.  
  86. void NETWORK::HostNewSession(char sessionName[])
  87. {
  88.     try
  89.     {
  90.         WCHAR strHost[128];
  91.         DPN_APPLICATION_DESC desc;
  92.         mbstowcs(strHost, sessionName, strlen(sessionName) + 1);
  93.         strcpy(activeSession, sessionName);
  94.  
  95.         // Set up the Application Description.
  96.         ZeroMemory(&desc, sizeof(DPN_APPLICATION_DESC));
  97.         desc.dwSize = sizeof(DPN_APPLICATION_DESC);
  98.         desc.dwFlags = DPNSESSION_CLIENT_SERVER;    // Flag describing the app
  99.         desc.guidApplication = RTS_APP_ID;          // GUID for the app
  100.         desc.pwszSessionName = strHost;                // Session name
  101.  
  102.         // Host the application.
  103.         if(FAILED(m_pServer->Host(&desc, &m_pMyAddress, 1, NULL, NULL, this, 0)))
  104.             debug.Print("Failed to host session");
  105.         else connected = true;
  106.     }
  107.     catch(...)
  108.     {
  109.         debug.Print("Error in NETWORK::HostNewSession()");
  110.     }
  111. }
  112.  
  113. void NETWORK::FindSessions()
  114. {
  115.     if(m_pClient == NULL)return;
  116.  
  117.     sessions.clear();
  118.  
  119.     //Setup what kind of sessions you are looking for
  120.     DPN_APPLICATION_DESC desc;
  121.     ZeroMemory(&desc, sizeof(DPN_APPLICATION_DESC));
  122.     desc.dwSize = sizeof(DPN_APPLICATION_DESC);
  123.     desc.guidApplication = RTS_APP_ID;
  124.  
  125.     if(FAILED(m_pClient->EnumHosts(&desc,        // pApplicationDesc
  126.                            m_pServerAddress,    // Host Address
  127.                            m_pMyAddress,        // Device Address
  128.                            NULL, 0,             // pvUserEnumData, size
  129.                            1,                   // dwEnumCount
  130.                            0,                   // dwRetryInterval
  131.                            0,                   // dwTimeOut
  132.                            NULL,                // pvUserContext
  133.                            NULL,                // pAsyncHandle
  134.                            DPNENUMHOSTS_SYNC))) // dwFlags
  135.         debug.Print("Failed to enumerate Sessions");
  136. }
  137.  
  138. HRESULT NETWORK::ConnectToSession(int index)
  139. {
  140.     if(index < 0 || index >= sessions.size() || m_pClient == NULL)
  141.     {
  142.         debug.Print("Not correct session to connect to, or m_pClient is NULL");
  143.         return E_FAIL;
  144.     }
  145.  
  146.     strcpy(activeSession, sessions[index].name.c_str());
  147.  
  148.     HRESULT hr = m_pClient->Connect(&sessions[index].desc,        // pdnAppDesc
  149.                                   sessions[index].address,      // pHostAddr
  150.                                   m_pMyAddress,                    // pDeviceInfo
  151.                                   NULL,                            // pdnSecurity
  152.                                   NULL,                            // pdnCredentials
  153.                                   NULL, 0,                        // pvUserConnectData, Size
  154.                                   NULL,                            // pvAsyncContext
  155.                                   NULL,                            // pvAsyncHandle
  156.                                   DPNCONNECT_SYNC);                // dwFlags
  157.  
  158.     if(SUCCEEDED(hr))connected = true;
  159.  
  160.     return hr;
  161. }
  162.  
  163. DP_PLAYER* NETWORK::FindPlayer(DPNID id)
  164. {
  165.     //Finds a player in the player vector with matching ID
  166.     for(int i=0;i<network.players.size();i++)
  167.         if(network.players[i].id == id)
  168.             return &network.players[i];
  169.  
  170.     return NULL;
  171. }
  172.  
  173. void NETWORK::Send(RTS_MSG *msg, bool guaranteed)
  174. {
  175.     //Sends the message to all players
  176.     SendTo(DPNID_ALL_PLAYERS_GROUP, msg, guaranteed);
  177. }
  178.  
  179. void NETWORK::SendTo(DPNID id, RTS_MSG *msg, bool guaranteed)
  180. {
  181.     try
  182.     {
  183.         DPN_BUFFER_DESC desc;
  184.         desc.pBufferData = (BYTE*)msg;
  185.         
  186.         //Message size
  187.         if(msg->type == GAME_MSG_TEXT)
  188.             desc.dwBufferSize = sizeof(MSG_TEXT);
  189.         else if(msg->type == GAME_MSG_COMMAND)
  190.             desc.dwBufferSize = sizeof(MSG_COMMAND);
  191.         else if(msg->type == GAME_MSG_PLAYER)
  192.             desc.dwBufferSize = sizeof(MSG_PLAYER);
  193.         else if(msg->type == GAME_MSG_TERRAIN)
  194.             desc.dwBufferSize = sizeof(MSG_TERRAIN);
  195.         else if(msg->type == GAME_MSG_OBJECT)
  196.             desc.dwBufferSize = sizeof(MSG_OBJECT);
  197.         else if(msg->type == GAME_MSG_ORDER)
  198.             desc.dwBufferSize = sizeof(MSG_ORDER);
  199.  
  200.         DWORD flags = DPNSEND_SYNC | DPNSEND_NOLOOPBACK;
  201.         if(guaranteed)flags |= DPNSEND_GUARANTEED;
  202.  
  203.         if(server && m_pServer)
  204.         {
  205.             m_pServer->SendTo(id,                          // dpnid
  206.                                 &desc,                      // pBufferDesc
  207.                                 1,                        // cBufferDesc
  208.                                 0,                        // dwTimeOut
  209.                                 NULL,                     // pvAsyncContext
  210.                                 NULL,                     // pvAsyncHandle
  211.                                 flags);                      // dwFlags                                
  212.         }
  213.         else if(m_pClient)
  214.         {
  215.             m_pClient->Send(&desc,                // pBufferDesc
  216.                             1,                      // cBufferDesc
  217.                             0,                      // dwTimeOut
  218.                             NULL,                   // pvAsyncContext
  219.                             NULL,                   // pvAsyncHandle
  220.                             flags);    // dwFlags
  221.         }
  222.     }
  223.     catch(...)
  224.     {
  225.         debug.Print("Error in NETWORK::SendTo()");
  226.     }
  227. }
  228.  
  229. HRESULT WINAPI ServerCallback(PVOID pvUserContext, DWORD dwMessageType, PVOID pMessage)
  230. {
  231.     try
  232.     {
  233.         switch(dwMessageType)
  234.         {
  235.             case DPN_MSGID_CREATE_PLAYER:        //New player has connected
  236.             {
  237.                 PDPNMSG_CREATE_PLAYER msg = (PDPNMSG_CREATE_PLAYER)pMessage;
  238.  
  239.                 if(msg->pvPlayerContext == &network)    //Local player
  240.                 {
  241.                     network.m_localID = msg->dpnidPlayer;
  242.                     network.players.push_back(DP_PLAYER(msg->dpnidPlayer, network.playerName));
  243.                     network.players[network.players.size() - 1].done = true;
  244.                 }
  245.                 else
  246.                 {
  247.                     //Ask for new players name
  248.                     MSG_COMMAND cmd(0, 0);
  249.                     network.SendTo(msg->dpnidPlayer, &cmd, true);
  250.                 }
  251.  
  252.                 break;
  253.             }
  254.             case DPN_MSGID_DESTROY_PLAYER:        //Player has left the game
  255.             {
  256.                 PDPNMSG_DESTROY_PLAYER msg = (PDPNMSG_DESTROY_PLAYER)pMessage;
  257.                 
  258.                 DP_PLAYER *player = network.FindPlayer(msg->dpnidPlayer);
  259.                 if(player != NULL)
  260.                 {
  261.                     MSG_PLAYER play(*player, 1);
  262.                     network.Send(&play, true);
  263.                     network.players.erase(player);
  264.                 }
  265.  
  266.                 break;
  267.             }
  268.             case DPN_MSGID_TERMINATE_SESSION:    //Session terminated
  269.             {
  270.                 network.players.clear();
  271.                 network.connected = false;
  272.  
  273.                 break;
  274.             }
  275.             case DPN_MSGID_RECEIVE:        //Data received
  276.             {
  277.                 PDPNMSG_RECEIVE data = (PDPNMSG_RECEIVE)pMessage;
  278.                 RTS_MSG *msg = (RTS_MSG*)data->pReceiveData;
  279.                 DP_PLAYER *player = network.FindPlayer(data->dpnidSender);
  280.  
  281.                 switch(msg->type)
  282.                 {
  283.                     case GAME_MSG_TEXT:        //Text message
  284.                     {
  285.                         MSG_TEXT *textMessage = (MSG_TEXT*)msg;
  286.                         network.chat.push_back(textMessage->text);
  287.                         if(network.chat.size() > 20)network.chat.erase(network.chat.begin());
  288.  
  289.                         //Relay text message to other players
  290.                         network.Send(msg, true);
  291.                         break;
  292.                     }
  293.                     case GAME_MSG_COMMAND:        //Game Command
  294.                     {
  295.                         MSG_COMMAND *cmd = (MSG_COMMAND*)msg;
  296.  
  297.                         if(cmd->command == 1)    //Next terrain piece
  298.                         {
  299.                             if(cmd->attribute * 100 < app.m_terrain.m_size.x * app.m_terrain.m_size.y)
  300.                             {
  301.                                 MSG_TERRAIN terr(app.m_terrain, cmd->attribute);
  302.                                 network.SendTo(data->dpnidSender, &terr, true);
  303.                             }
  304.                             else if(player != NULL)    //Start transmitting objects
  305.                             {
  306.                                 MSG_OBJECT obj(app.m_terrain.m_objects[0], 0);
  307.                                 network.SendTo(data->dpnidSender, &obj, true);
  308.                             }
  309.                         }
  310.                         else if(cmd->command == 2)    //Next Terrain Object
  311.                         {
  312.                             if(cmd->attribute >= app.m_terrain.m_objects.size())    //Done
  313.                             {
  314.                                 player->done = true;
  315.  
  316.                                 //Check that all players are done
  317.                                 bool allDone = true;
  318.                                 for(int i=0;i<network.players.size();i++)
  319.                                     if(!network.players[i].done)
  320.                                         allDone = false;
  321.  
  322.                                 if(allDone)        //Start game
  323.                                 {
  324.                                     for(int i=1;i<network.players.size();i++)    //Add players
  325.                                     {
  326.                                         MSG_COMMAND cplay(3, i);
  327.                                         network.SendTo(network.players[i].id, &cplay, true);
  328.                                     }
  329.  
  330.                                     MSG_COMMAND cmd(4, 0);
  331.                                     network.Send(&cmd, true);
  332.                                     app.m_lobby.room = 10;
  333.  
  334.                                     srand(0);
  335.                                     app.AddPlayers(network.players.size(), 0);
  336.                                 }
  337.                             }
  338.                             else        //Send next object
  339.                             {
  340.                                 MSG_OBJECT obj(app.m_terrain.m_objects[cmd->attribute], cmd->attribute);
  341.                                 network.SendTo(data->dpnidSender, &obj, true);
  342.                             }
  343.                         }
  344.  
  345.                         break;
  346.                     }
  347.                     case GAME_MSG_PLAYER:        //Add, Remove players etc...
  348.                     {        
  349.                         MSG_PLAYER *playerMessage = (MSG_PLAYER*)msg;
  350.  
  351.                         if(playerMessage->operation == 2)        //Get Name
  352.                         {
  353.                             network.players.push_back(DP_PLAYER(data->dpnidSender, playerMessage->player.name));
  354.  
  355.                             //Send all other players to the new player...
  356.                             for(int i=0;i<network.players.size();i++)
  357.                             {
  358.                                 MSG_PLAYER ply(network.players[i], 0);
  359.                                 network.SendTo(data->dpnidSender, &ply, true);
  360.                             }
  361.  
  362.                             MSG_PLAYER newPlayer(network.players[network.players.size() - 1], 0);
  363.                             network.Send(&newPlayer, true);
  364.                         }
  365.                         break;
  366.                     }
  367.                     case GAME_MSG_ORDER:        //Unit Order
  368.                     {
  369.                         app.HandleNetworkOrder(msg);                        
  370.                         network.Send(msg, true);    //Relay order
  371.                         break;
  372.                     }
  373.                 }
  374.  
  375.                 break;
  376.             }
  377.         }
  378.  
  379.         return S_OK;
  380.     }
  381.     catch(...)
  382.     {
  383.         debug.Print("Error in ServerCallback()");
  384.         return E_FAIL;
  385.     }
  386. }
  387.  
  388. HRESULT WINAPI ClientCallback(PVOID pvUserContext, DWORD dwMessageType, PVOID pMessage)
  389. {
  390.     try
  391.     {
  392.         switch(dwMessageType)
  393.         {
  394.             case DPN_MSGID_ENUM_HOSTS_RESPONSE:        // Enumerate sessions responses
  395.             {
  396.                 PDPNMSG_ENUM_HOSTS_RESPONSE msg = (PDPNMSG_ENUM_HOSTS_RESPONSE)pMessage;
  397.                 const DPN_APPLICATION_DESC* desc = msg->pApplicationDescription;
  398.                 char sessionName[128];
  399.                 wcstombs(sessionName, desc->pwszSessionName, wcslen(desc->pwszSessionName) + 1);
  400.                 network.sessions.push_back(SESSION(sessionName, *desc, msg->pAddressSender));
  401.                 break;
  402.             }
  403.             case DPN_MSGID_TERMINATE_SESSION:        //Server died, or some such...
  404.             {
  405.                 network.players.clear();
  406.                 network.connected = false;
  407.  
  408.                 break;
  409.             }
  410.             case DPN_MSGID_RECEIVE:            //Data...
  411.             {
  412.                 PDPNMSG_RECEIVE data = (PDPNMSG_RECEIVE)pMessage;
  413.                 RTS_MSG *msg = (RTS_MSG*)data->pReceiveData;
  414.                 DP_PLAYER *player = network.FindPlayer(data->dpnidSender);
  415.  
  416.                 switch(msg->type)
  417.                 {
  418.                     case GAME_MSG_TEXT:        //Chat/text message
  419.                     {
  420.                         MSG_TEXT *textMessage = (MSG_TEXT*)msg;
  421.                         network.chat.push_back(textMessage->text);
  422.                         if(network.chat.size() > 20)network.chat.erase(network.chat.begin());
  423.  
  424.                         break;
  425.                     }
  426.                     case GAME_MSG_COMMAND:        //Game command
  427.                     {
  428.                         MSG_COMMAND *cmd = (MSG_COMMAND*)msg;
  429.  
  430.                         if(cmd->command == 0)        //Ask for name
  431.                         {
  432.                             MSG_PLAYER play(DP_PLAYER(0, network.playerName), 2);
  433.                             network.Send(&play, true);
  434.                         }
  435.                         else if(cmd->command == 3)    //Add players
  436.                         {
  437.                             srand(0);
  438.                             app.m_terrain.InitPathfinding();
  439.                             app.AddPlayers(network.players.size(), cmd->attribute);
  440.                         }
  441.                         else if(cmd->command == 4)    //Start game
  442.                         {
  443.                             app.m_lobby.room = 10;
  444.                             app.m_terrain.m_updateTerrain = true;        //Creates patches, pathfinding etc...
  445.                         }
  446.  
  447.                         break;
  448.                     }
  449.                     case GAME_MSG_PLAYER:        //Add, Remove, Update Name of players...
  450.                     {
  451.                         MSG_PLAYER *playerMessage = (MSG_PLAYER*)msg;
  452.  
  453.                         if(playerMessage->operation == 0)    //Add Player
  454.                         {
  455.                             DP_PLAYER *oldPlayer = network.FindPlayer(playerMessage->player.id);
  456.  
  457.                             if(oldPlayer == NULL)
  458.                             {
  459.                                 network.players.push_back(playerMessage->player);
  460.                                 printf("Player %s joined game\n", playerMessage->player.name);
  461.                             }
  462.                         }
  463.                         else if(playerMessage->operation == 1)    //Remove Player
  464.                         {
  465.                             DP_PLAYER *playerToErase = network.FindPlayer(playerMessage->player.id);
  466.                             if(playerToErase != NULL)network.players.erase(playerToErase);
  467.                         }
  468.                         if(playerMessage->operation == 2 && player)        //Update Name
  469.                             strcpy(player->name, playerMessage->player.name);
  470.  
  471.                         break;
  472.                     }
  473.                     case GAME_MSG_TERRAIN:        //Receive heightmap from server
  474.                     {
  475.                         MSG_TERRAIN *terr = (MSG_TERRAIN*)msg;
  476.  
  477.                         app.m_lobby.room = 5;
  478.  
  479.                         for(int i=terr->packageNo * 100, i2=0;i2<100 && i < terr->size.x * terr->size.y;i++, i2++)
  480.                             app.m_terrain.m_pHeightMap->m_pHeightMap[i] = terr->hm[i2];
  481.  
  482.                         MSG_COMMAND cmd(1, terr->packageNo + 1);
  483.                         network.Send(&cmd, true);
  484.  
  485.                         break;
  486.                     }
  487.                     case GAME_MSG_OBJECT:        //Add terrain objects
  488.                     {
  489.                         MSG_OBJECT *obj = (MSG_OBJECT*)msg;
  490.                         if(obj->packageNo == 0)
  491.                             app.m_terrain.m_objects.clear();
  492.  
  493.                         app.m_terrain.m_objects.push_back(OBJECT(obj->objType, obj->mp, obj->pos, obj->rot, obj->sca));
  494.  
  495.                         MSG_COMMAND cmd(2, obj->packageNo + 1);
  496.                         network.Send(&cmd, true);
  497.  
  498.                         break;
  499.                     }
  500.                     case GAME_MSG_ORDER:        //Unit Order
  501.                     {
  502.                         app.HandleNetworkOrder(msg);
  503.                         break;
  504.                     }
  505.                 }
  506.  
  507.                 break;
  508.             }
  509.         }
  510.  
  511.         return S_OK;
  512.     }
  513.     catch(...)
  514.     {
  515.         debug.Print("Error in ClientCallback()");
  516.         return E_FAIL;
  517.     }
  518. }